package org.zjvis.datascience.service.graph;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.zjvis.datascience.common.dto.graph.GraphFilterDTO;
import org.zjvis.datascience.common.dto.graph.GraphFilterPipelineDTO;
import org.zjvis.datascience.common.dto.graph.GraphFilterPipelineInstanceDTO;
import org.zjvis.datascience.common.exception.BaseErrorCode;
import org.zjvis.datascience.common.exception.DataScienceException;
import org.zjvis.datascience.common.graph.enums.GraphFilterSubTypeEnum;
import org.zjvis.datascience.common.graph.enums.GraphFilterTypeEnum;
import org.zjvis.datascience.common.graph.util.GraphUtil;
import org.zjvis.datascience.common.graph.filter.FilterResult;
import org.zjvis.datascience.common.graph.filter.FilterTree;
import org.zjvis.datascience.common.graph.filter.FilterTreeNode;
import org.zjvis.datascience.common.graph.filter.GraphFilterUtil;
import org.zjvis.datascience.common.graph.model.MetricResultManager;
import org.zjvis.datascience.common.model.ApiResultCode;
import org.zjvis.datascience.common.util.JwtUtil;
import org.zjvis.datascience.common.vo.graph.CategoryAttrVO;
import org.zjvis.datascience.common.vo.graph.CategoryVO;
import org.zjvis.datascience.common.vo.graph.GraphFilterVO;
import org.zjvis.datascience.service.mapper.GraphFilterMapper;

import java.lang.reflect.Field;
import java.sql.Types;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @description GraphFilterService
 * @date 2021-12-29
 */
@Service
public class GraphFilterService {
    private static final Logger logger = LoggerFactory.getLogger(GraphFilterService.class);

    @Autowired
    private GraphFilterMapper graphFilterMapper;

    @Autowired
    private JanusGraphEmbedService janusGraphEmbedService;

    @Autowired
    private GraphFilterPipelineInstanceService graphFilterPipelineInstanceService;

    @Autowired
    private GraphFilterPipelineService graphFilterPipelineService;

    public Long save(GraphFilterDTO dto) {
        graphFilterMapper.save(dto);
        return dto.getId();
    }

    public void update(GraphFilterDTO dto) {
        graphFilterMapper.update(dto);
    }

    public void deleteById(Long id) {
        graphFilterMapper.deleteById(id);
    }

    public void deleteByGraphFilterPipelineId(Long id) {
        graphFilterMapper.deleteByGraphFilterPipelineId(id);
    }

    public void deleteByGraphId(Long graphId) {
        graphFilterMapper.deleteByGraphId(graphId);
    }

    public GraphFilterDTO queryById(Long id) {
        return graphFilterMapper.queryById(id);
    }

    public List<GraphFilterDTO> queryByGraphFilterPipelineId(Long id) {
        return graphFilterMapper.queryByGraphFilterPipelineId(id);
    }

    public List<GraphFilterDTO> queryByParentId(Long id) {
        return graphFilterMapper.queryByParentId(id);
    }

    public Long queryUserById(Long id) {
        return graphFilterMapper.queryUserById(id);
    }

    public GraphFilterDTO load(GraphFilterVO vo) {
        baseInitParams(vo);
        queryParams(vo);
        return vo.dto();
    }

    public GraphFilterDTO loadAndSave(GraphFilterVO vo) {
        GraphFilterDTO dto = load(vo);
        Long id = this.save(dto);
        if (vo.getParentId() == null) {
            return dto;
        }
        GraphFilterDTO parent = this.queryById(vo.getParentId());

        int subType = parent.getSubType();
        if (subType != GraphFilterSubTypeEnum.OPERATION_OR.getVal()
                && subType != GraphFilterSubTypeEnum.OPERATION_AND.getVal()
        ) {
            List<GraphFilterDTO> childs = this.queryByParentId(vo.getParentId());
            if (childs != null && childs.size() > 0) {
                //中间插入，修改子节点父节点
                for (GraphFilterDTO child: childs) {
                    if (child.getId().equals(id)) {
                        continue;
                    }
                    child.setParentId(id);
                    this.update(child);
                }
            }
        }
        return dto;
    }

    public void deleteFilter(Long id) {
        GraphFilterDTO filter = this.queryById(id);
        int subType = filter.getSubType();
        if (subType != GraphFilterSubTypeEnum.OPERATION_OR.getVal()
                && subType != GraphFilterSubTypeEnum.OPERATION_AND.getVal()
        ) {
            Long parentId = filter.getParentId();
            List<GraphFilterDTO> childs = this.queryByParentId(id);
            for (GraphFilterDTO child: childs) {
                child.setParentId(parentId);
                this.update(child);
            }
            this.deleteById(id);
        }
        else {
            recursionDelete(id);
        }
    }

    public void recursionDelete(Long id) {
        this.deleteById(id);
        List<GraphFilterDTO> childFilter = this.queryByParentId(id);
        if (childFilter == null || childFilter.size() == 0) {
            return;
        } else {
            for (GraphFilterDTO filter: childFilter) {
                recursionDelete(filter.getId());
            }
        }
    }

    protected void baseInitParams(GraphFilterVO vo) {
        JSONObject data = vo.getData();
        data.put("input", new JSONArray());
        data.put("output", new JSONArray());

        int subType = vo.getSubType();
        GraphFilterSubTypeEnum subTypeEnum = GraphFilterUtil.getGraphFilterSubTypeEnum(subType);
        String tplFileName = subTypeEnum.getTplFileName();
        String subTypeName = subTypeEnum.getDesc();
        vo.setName(subTypeName);
        data.put("subTypeName", vo.getName());
        JSONArray setParams = GraphFilterUtil.getTemplateParams(tplFileName);

        data.put("setParams", setParams);
        vo.setData(data);
    }



    public void queryParams(GraphFilterVO vo) {
        int subType = vo.getSubType();
        int type = vo.getType();
        List<String> filter = null;
        Long graphId = vo.getGraphId();
        JSONArray setParams = vo.getData().getJSONArray("setParams");

        if (subType == GraphFilterSubTypeEnum.NODE_CATEGORY.getVal()) {
            List<String> ret = janusGraphEmbedService.queryVertexLabel(graphId, filter);
            setParams.getJSONObject(0).put("default", ret);
            setParams.getJSONObject(0).put("value", ret);
        }
        else if (subType == GraphFilterSubTypeEnum.LINK_CATEGORY.getVal()) {
            List<String> ret = janusGraphEmbedService.queryEdgeLabel(graphId, filter);
            setParams.getJSONObject(0).put("default", ret);
            setParams.getJSONObject(0).put("value", ret);
        }
        else if (subType == GraphFilterSubTypeEnum.LINK_WEIGHT_RANGE.getVal()) {
            Map<String, Object> ret = janusGraphEmbedService.queryEdgeWeightRange(graphId, filter);
            if (ret == null) {
                return;
            }
            setParams.getJSONObject(0).put("max", ret.get("max"));
            setParams.getJSONObject(0).put("min", ret.get("min"));
            List<Object> value = new ArrayList<>();
            value.add(ret.get("min"));
            value.add(ret.get("max"));
            setParams.getJSONObject(0).put("value", value);
            setParams.getJSONObject(0).put("step", calStep(ret.get("max"), ret.get("min")));
        }
        else if (subType == GraphFilterSubTypeEnum.LINK_ONLY_DIRECTED.getVal()) {

        }
        else if (subType == GraphFilterSubTypeEnum.LINK_ONLY_UNDIRECTED.getVal()) {

        }
        else if (subType == GraphFilterSubTypeEnum.LINK_ONLY_DOUBLE_DIRECTED.getVal()) {

        }
        else if (subType == GraphFilterSubTypeEnum.LINK_REMOVE_SELF_LOOP.getVal()) {

        }
        else if (type == GraphFilterTypeEnum.ATTR.getVal()) {
            if (subType == GraphFilterSubTypeEnum.ATTR_EQUAL.getVal()) {

            }
            else if (subType == GraphFilterSubTypeEnum.ATTR_RANGE.getVal()) {
                String attr = vo.getData().getString("attr");
                Map<String, Object> ret = janusGraphEmbedService.queryAttrRange(graphId, filter, attr);
                if (ret == null) {
                    return;
                }
                setParams.getJSONObject(0).put("max", ret.get("max"));
                setParams.getJSONObject(0).put("min", ret.get("min"));
                List<Object> value = new ArrayList<>();
                value.add(ret.get("min"));
                value.add(ret.get("max"));
                setParams.getJSONObject(0).put("value", value);
                if (attr.equals(MetricResultManager.DegreeTag)) {
                    setParams.getJSONObject(0).put("step", 1);
                } else {
                    setParams.getJSONObject(0).put("step", calStep(ret.get("max"), ret.get("min")));
                }
            }
            else if (subType == GraphFilterSubTypeEnum.ATTR_NOT_NULL.getVal()) {

            }
            else if (subType == GraphFilterSubTypeEnum.ATTR_CATEGORY.getVal()) {
                List<String> ret = janusGraphEmbedService.queryAttrDedup(graphId, filter, vo.getData().getString("attr"));
                setParams.getJSONObject(0).put("default", ret);
            }
            else if (subType == GraphFilterSubTypeEnum.ATTR_CATEGORY_COUNT.getVal()) {
                Map<Object, Long> ret = janusGraphEmbedService.queryAttrGroupCount(graphId, filter, vo.getData().getString("attr"));
//                ret = new LinkedHashMap<>(ret);
                if (ret.size() == 0) {
                    return;
                }
                Map.Entry<Object, Long> head = ret.entrySet().iterator().next();
                Map.Entry<Object, Long> tail;
                try {
                    Field tailField = ret.getClass().getDeclaredField("tail");
                    tailField.setAccessible(true);
                    tail = (Map.Entry<Object, Long>) tailField.get(ret);
                } catch (Exception e) {
                    return;
                }
                vo.getData().put("count", ret);
                Long max = head.getValue();
                Long min = tail.getValue();
                setParams.getJSONObject(0).put("max", max);
                setParams.getJSONObject(0).put("min", min);
                List<Object> value = new ArrayList<>();
                value.add(min);
                value.add(max);
                setParams.getJSONObject(0).put("value", value);
                setParams.getJSONObject(0).put("step", 1);
            }
            else if (subType == GraphFilterSubTypeEnum.ATTR_INNER_LINK.getVal()
                    || subType == GraphFilterSubTypeEnum.ATTR_OUTER_LINK.getVal()) {
                Map<Object, Long> ret = janusGraphEmbedService.queryAttrGroupCount(graphId, filter, vo.getData().getString("attr"));
                Double countSum = ret.values().stream().mapToDouble(v -> v).sum();
                JSONArray param = new JSONArray();
                Iterator<Map.Entry<Object, Long>> iterator = ret.entrySet().iterator();
                while (iterator.hasNext()) {
                    Map.Entry<Object, Long> entry = iterator.next();
                    JSONObject obj = new JSONObject();
                    obj.put("value", entry.getKey());
                    obj.put("proportion", GraphUtil.round2DecimalDouble(entry.getValue() / countSum));
                    param.add(obj);
                }
                setParams.getJSONObject(0).put("default", param);
            }
        }
        else if (subType == GraphFilterSubTypeEnum.TOPOLOGY_DEGREE_RANGE.getVal()
                || subType == GraphFilterSubTypeEnum.TOPOLOGY_IN_DEGREE_RANGE.getVal()
                || subType == GraphFilterSubTypeEnum.TOPOLOGY_OUT_DEGREE_RANGE.getVal()) {
            Map<String, Object> ret = janusGraphEmbedService.queryDegreeRange(graphId, filter, subType);
            if (ret == null) {
                return;
            }
            setParams.getJSONObject(0).put("max", ret.get("max"));
            setParams.getJSONObject(0).put("min", ret.get("min"));
            List<Object> value = new ArrayList<>();
            value.add(ret.get("min"));
            value.add(ret.get("max"));
            setParams.getJSONObject(0).put("value", value);
            setParams.getJSONObject(0).put("step", 1);
        }
        else if (subType == GraphFilterSubTypeEnum.TOPOLOGY_SELF_LOOP.getVal()) {

        }
    }

    public Long executeFilterPipeline(Long graphId, Long graphFilterPipelineId, List<CategoryVO> categories, List<String> activateMetric) {
        GraphFilterPipelineDTO pipeline = graphFilterPipelineService.queryById(graphFilterPipelineId);
        GraphFilterPipelineInstanceDTO instance = new GraphFilterPipelineInstanceDTO(pipeline);
        List<GraphFilterDTO> filters = this.queryByGraphFilterPipelineId(graphFilterPipelineId);
        JSONObject dataJson = new JSONObject();
        dataJson.put("filters", filters);
        instance.setDataJson(dataJson.toJSONString());
        graphFilterPipelineInstanceService.save(instance);
        graphFilterPipelineInstanceService.setStatusRunning(instance);
        long _start = System.currentTimeMillis();


        //生成遍历树
        Map<Long, GraphFilterDTO> filterMap = new HashMap<>();
        Map<Long, Long> parentMap = new HashMap<>();
        Map<Long, List<Long>> childMap = new HashMap<>();
        Long rootId = null;
        for (GraphFilterDTO filter: filters) {
            Long id = filter.getId();
            Long parentId = filter.getParentId();
            filterMap.put(id, filter);
            if (parentId == null) {
                rootId = id;
            } else {
                parentMap.put(id, parentId);
                childMap.putIfAbsent(parentId, new ArrayList<>());
                childMap.get(parentId).add(id);
            }
        }
        if (rootId == null) {
            long _end = System.currentTimeMillis();
            graphFilterPipelineInstanceService.setStatusSuccess(instance, _end - _start);
            return null;
        }
        FilterTreeNode<Long> root = new FilterTreeNode<>(rootId);
        produceFilterTree(root, childMap);
        //checkNotTree
        Stack<FilterTreeNode<Long>> notTreeStack = checkNotTree(root, filterMap);
        if (notTreeStack.size() > 0) {
            while (!notTreeStack.isEmpty()) {
                FilterTreeNode<Long> notNode = notTreeStack.pop();
                if (notNode.children.size() > 0) {
                    FilterResult filterResult = executeFilterTree(notNode.children.get(0), filterMap, graphId, categories, activateMetric);
                    GraphFilterDTO notFilter = filterMap.get(notNode.value);
                    JSONObject filterData = JSONObject.parseObject(notFilter.getDataJson());
                    filterData.put("notInput", filterResult);
                    notFilter.setDataJson(filterData.toJSONString());
                    notNode.children.clear();
                }
            }
        }

        FilterResult filterResult = executeFilterTree(root, filterMap, graphId, categories, activateMetric);
        long _end = System.currentTimeMillis();
        dataJson.put("filterResult", filterResult);
        instance.setDataJson(dataJson.toJSONString());
        graphFilterPipelineInstanceService.setStatusSuccess(instance, _end - _start);
        return instance.getId();
    }

    public List<Long> checkFilterPath(List<Long> path, Map<Long, GraphFilterDTO> filterMap, List<CategoryVO> categories, List<String> activateMetric) {
        List<Long> errorFilterId = new ArrayList<>();
        Set<String> validAttr = categories.stream().map(CategoryVO::getAttrs).flatMap(attrs->attrs.stream().map(CategoryAttrVO::getName)).collect(Collectors.toSet());
        for (Long id: path) {
            GraphFilterDTO filter = filterMap.get(id);
            if (filter.getSubType() == GraphFilterSubTypeEnum.NODE_CATEGORY.getVal()) {
                JSONArray setParams = JSONObject.parseObject(filter.getDataJson()).getJSONArray("setParams");
                List<String> labels = setParams.getJSONObject(0).getJSONArray("value").toJavaList(String.class);
                Set<String> filterAttrs = categories.stream()
                        .filter(c->labels.contains(c.getId()))
                        .map(CategoryVO::getAttrs).flatMap(attrs->attrs.stream().map(CategoryAttrVO::getName)).collect(Collectors.toSet());
                validAttr.retainAll(filterAttrs);
            }
            if (filter.getType() == GraphFilterTypeEnum.ATTR.getVal()) {
                String key = JSONObject.parseObject(filter.getDataJson()).getString("attr");
                if (!validAttr.contains(key) && !activateMetric.contains(key)) {
                    errorFilterId.add(id);
                }
            }
        }
        return errorFilterId;
    }

    public void produceFilterTree(FilterTreeNode<Long> root, Map<Long, List<Long>> childMap) {
        List<Long> children = childMap.get(root.value);
        if (children == null) {
            return;
        }
        for (Long childId: children) {
            FilterTreeNode<Long> child = new FilterTreeNode<>(childId);
            root.children.add(child);
            produceFilterTree(child, childMap);
        }
    }

    /***
     * 组合结果伪代码
     * init mixStack;
     * while stack not empty:
     *    cResult = stack.pop
     *    if cResult is not operation:
     *          mixStack.push(cResult)
     *    else:
     *          opSize = operation.getSize
     *          mixResult = mixStack.pop
     *          for i<opSize - 1:
     *              pResult = mixStack.pop
     *              mixResult.operation(pResult)
     *          mixStack.push(mixResult)
     *  (assert mixStack.size=1)
     * return mixStack.pop
     * @param filterResultStack
     * @return
     */
    public FilterResult mixFilterResult(Stack<FilterResult> filterResultStack) {
        Stack<FilterResult> mixStack = new Stack<>();
        while (!filterResultStack.isEmpty()) {
            FilterResult cResult = filterResultStack.pop();
            if (!cResult.getOperation()) {
                mixStack.push(cResult);
            } else {
                List<Long> filterPath = cResult.getFilterPath();
                int opSize = filterPath.size();
                if (opSize > mixStack.size()) {
                    //叶子节点操作符，无效
                    continue;
                }
                Long opType = filterPath.get(0);
                FilterResult mixResult = mixStack.pop();
                for (int i = 0; i < opSize - 1; i++) {
                    FilterResult pResult = mixStack.pop();
                    if (opType.equals(FilterTree.AND_VALUE)) {
                        mixResult.and(pResult);
                    } else if (opType.equals(FilterTree.OR_VALUE)) {
                        mixResult.or(pResult);
                    }
                }
                mixStack.push(mixResult);
            }
        }
        assert mixStack.size() == 1;
        return mixStack.pop();
    }

    public Set<String> simpleTraceBackCategory(Set<String> filterCid, Long parentId) {
        if (parentId != null) {
            GraphFilterDTO filter = this.queryById(parentId);
            while (filter != null) {
                if (filter.getSubType() == GraphFilterSubTypeEnum.NODE_CATEGORY.getVal()) {
                    JSONArray setParams = JSONObject.parseObject(filter.getDataJson()).getJSONArray("setParams");
                    List<String> labels = setParams.getJSONObject(0).getJSONArray("value").toJavaList(String.class);
                    filterCid.retainAll(labels);
                }
                filter = this.queryById(filter.getParentId());
            }
        }
        return filterCid;
    }

    public JSONObject queryFilterAvailableAttrs(List<CategoryVO> categories, Long parentId, List<String> activateMetric) {
        Set<String> filterCid = categories.stream().map(CategoryVO::getId).collect(Collectors.toSet());
        simpleTraceBackCategory(filterCid, parentId);
        List<List<CategoryAttrVO>> filterAttrs = categories.stream()
                .filter(c->filterCid.contains(c.getId()))
                .map(CategoryVO::getAttrs).collect(Collectors.toList());
        Set<CategoryAttrVO> shareAttrs = new HashSet<>();
        if (filterAttrs.size() > 0) {
            shareAttrs = new HashSet<>(filterAttrs.get(0));
            for (List<CategoryAttrVO> attr: filterAttrs) {
                Set<CategoryAttrVO> attrSet = new HashSet<>(attr);
                shareAttrs.retainAll(attrSet);
            }
        }


        Set<String> shareAttr = shareAttrs.stream().map(CategoryAttrVO::getName).collect(Collectors.toSet());
        Set<String> numAttr = shareAttrs.stream().filter(x->x.getType() == Types.NUMERIC).map(CategoryAttrVO::getName).collect(Collectors.toSet());
        Set<String> stringAttr = shareAttrs.stream().filter(x->x.getType() == Types.VARCHAR).map(CategoryAttrVO::getName).collect(Collectors.toSet());
        if (activateMetric.size() > 0) {
            shareAttr.addAll(activateMetric);
            if (activateMetric.contains(MetricResultManager.ClusterTag)) {
                stringAttr.add(MetricResultManager.ClusterTag);
                activateMetric.remove(MetricResultManager.ClusterTag);
            }
            numAttr.addAll(activateMetric);
        }
        JSONObject obj = new JSONObject();
        obj.put(String.valueOf(GraphFilterSubTypeEnum.ATTR_EQUAL.getVal()), shareAttr);
        obj.put(String.valueOf(GraphFilterSubTypeEnum.ATTR_RANGE.getVal()), numAttr);
        obj.put(String.valueOf(GraphFilterSubTypeEnum.ATTR_NOT_NULL.getVal()), shareAttr);
        obj.put(String.valueOf(GraphFilterSubTypeEnum.ATTR_CATEGORY.getVal()), stringAttr);
        obj.put(String.valueOf(GraphFilterSubTypeEnum.ATTR_CATEGORY_COUNT.getVal()), stringAttr);
        obj.put(String.valueOf(GraphFilterSubTypeEnum.ATTR_INNER_LINK.getVal()), shareAttr);
        obj.put(String.valueOf(GraphFilterSubTypeEnum.ATTR_OUTER_LINK.getVal()), shareAttr);

        return obj;
    }

    public void updateFilter(GraphFilterVO vo) {
        //更新过滤器信息，可改变类型
        Long filterId = vo.getId();
        GraphFilterDTO oldFilter = this.queryById(filterId);
        if (vo.getData().isEmpty()) {
            throw new DataScienceException(ApiResultCode.PARAM_ERROR, "data为空");
        }
        if (!vo.getData().containsKey("setParams")) {
            load(vo);
        }
        this.update(vo.dto());
    }

    public Double calStep(Object max, Object min) {
        return calStep(max, min, 10);
    }

    public Double calStep(Object max, Object min, int num) {
        Double dmax = Double.parseDouble(max.toString());
        Double dmin = Double.parseDouble(min.toString());
        return (dmax - dmin) / num;
    }

    public Stack<FilterTreeNode<Long>> checkNotTree(FilterTreeNode<Long> root, Map<Long, GraphFilterDTO> filterMap) {
        Stack<FilterTreeNode<Long>> notTreeStack = new Stack<>();
        if (root == null) {
            return null;
        }
        Queue<FilterTreeNode<Long>> queue = new LinkedList<>();
        queue.add(root);
        while (!queue.isEmpty()) {
            FilterTreeNode<Long> node = queue.poll();
            GraphFilterDTO filter  = filterMap.get(node.value);
            for (int i = 0; i < node.children.size(); i++) {
                FilterTreeNode<Long> child = node.children.get(i);
                GraphFilterDTO childFilter = filterMap.get(child.value);
                if (filter.getSubType().equals(GraphFilterSubTypeEnum.OPERATION_NOT.getVal())) {
                    if (childFilter.getSubType().equals(GraphFilterSubTypeEnum.OPERATION_OR.getVal())
                            || childFilter.getSubType().equals(GraphFilterSubTypeEnum.OPERATION_AND.getVal())) {
                        notTreeStack.add(node);
                    }
                }
                queue.add(child);
            }
        }
        return notTreeStack;
    }

    public FilterResult executeFilterTree(FilterTreeNode<Long> root, Map<Long, GraphFilterDTO> filterMap, Long graphId, List<CategoryVO> categories, List<String> activateMetric) {
        FilterTree filterTree = new FilterTree(root, filterMap);
        filterTree.dfs();
        Stack<List<Long>> pathStack = filterTree.getPathStack();
        Stack<FilterResult> filterResultStack = new Stack<>();

        for (List<Long> path: pathStack) {
            FilterResult filterResult;
            if (!path.contains(FilterTree.AND_VALUE) && !path.contains(FilterTree.OR_VALUE)) {
                List<Long> errorId = checkFilterPath(path, filterMap, categories, activateMetric);
                if (errorId.size() > 0) {
                    throw DataScienceException.of(BaseErrorCode.GRAPH_FILTER_ERROR, null, errorId.toString());
                }
                filterResult = janusGraphEmbedService.filterOnePath(graphId, path, filterMap);
            } else {
                filterResult = new FilterResult(path);
            }
            filterResultStack.push(filterResult);
        }
        FilterResult filterResult = mixFilterResult(filterResultStack);
        janusGraphEmbedService.fixFilterResult(graphId, filterResult);
        return filterResult;
    }

    public void checkAuth(Long id) {
        long userId = JwtUtil.getCurrentUserId();
        if (userId==0) {
            throw new DataScienceException(BaseErrorCode.UNAUTHORIZED);
        }
        Long owner = queryUserById(id);
        if (owner == null || JwtUtil.getCurrentUserId() != owner) {
            throw new DataScienceException(BaseErrorCode.GRAPH_NO_PERMISSION);
        }
    }

    public void checkFilterAndPipeline(Long id, Long pipelineId, Long graphId){
        GraphFilterDTO filter = queryById(id);
        Long pipeline = filter.getGraphFilterPipelineId();
        Long graph = filter.getGraphId();
        if (pipeline == null || graph == null) {
            throw new DataScienceException(BaseErrorCode.UNAUTHORIZED);
        }
        if (pipelineId == null) {
            if (!graph.equals(graphId)) {
                throw new DataScienceException(BaseErrorCode.GRAPH_FILTER_RELATION_ERROR);
            }
        } else {
            if (!pipeline.equals(pipelineId)) {
                throw new DataScienceException(BaseErrorCode.GRAPH_FILTER_RELATION_ERROR);
            }
        }
    }
}
